The use of object-oriented programming techniques can facilitate the development of complex computer programs. Programming languages that support object-oriented techniques have been developed. One such programming language is C++.
Two common characteristics of object-oriented programming languages are support for data encapsulation and data type inheritance. Data encapsulation refers to the binding of functions and data. Inheritance refers to the ability to declare a data type in terms of other data types.
In the C++ language, object-oriented techniques are supported through the use classes. A class is a user-defined type. A class declaration describes the data members and function members of the class. For example, the following declaration defines data members and a function member of a class named CIRCLE.
______________________________________ class CIRCLE { int x, y; int radius; void draw(); }; ______________________________________
Variables x and y specify the center location of a circle and variable radius specifies the radius of the circle. These variables are referred to as data members of the class CIRCLE. The function draw is a user-defined function that draws the circle of the specified radius at the specified location. The function draw is referred to as a function member of class CIRCLE. The data members and function members of a class are bound together in that the function operates an instance of the class. An instance of a class is also called an object of the class.
In the syntax of C++, the following statement declares the objects a and b to be of type class CIRCLE.
CIRCLE a, b;
This declaration causes the allocation of memory for the objects a and b, such an allocation is called an instance of the class. The following statements assign data to the data members of objects a and b.
______________________________________ a.x = 2; a.y = 2; a.radius = 1; b.x = 4; b.y = 5; b.radius = 2; ______________________________________
The following statements are used to draw the circles defined by objects a and b.
a.draw();
b.draw();
A derived class is a class that inherits the characteristics--data members and function members--of its base classes. For example, the following derived class CIRCLE.sub.-- FILL inherits the characteristics of the base class CIRCLE.
______________________________________ class CIRCLE.sub.-- FILL : CIRCLE { int pattern; void fill(); }; ______________________________________
This declaration specifies that class CIRCLE.sub.-- FILL includes all the data and function members that are in class CIRCLE in addition to the those data and function members introduced in the declaration of class CIRCLE.sub.-- FILL, that is, data member pattern and function member fill. In this example, class CIRCLE.sub.-- FILL would have data members x, y, radius, and pattern and function members draw and fill. Class CIRCLE.sub.-- FILL is said to "inherit" the characteristics of class CIRCLE. A class that inherits the characteristics of another class is a derived class (e.g., CIRCLE.sub.-- FILL). A class that does not inherit the characteristics of another class is a primary class (e.g., CIRCLE). A class whose characteristics are inherited by another class is a base class (e.g., CIRCLE is a base class of CIRCLE.sub.-- FILL). A derived class may inherit the characteristics of several classes, that is, a derived class may have several base classes. This is referred to as multiple inheritance.
A derived class may specify that a base class is to be inherited virtually. Virtual inheritance of a base class means that only one instance of the virtual base class exists in the derived class. For example, the following is an example of a derived class with two non-virtual base classes.
class PATTERN: CIRCLE, CIRCLE{ . . . };
In this declaration class PATTERN inherits class CIRCLE twice non-virtually. There are two instances of class CIRCLE in class PATTERN.
The following is an example of a derived class with two virtual base classes.
class PATTERN: virtual CIRCLE, virtual CIRCLE{ . . . };
The derived class PATTERN inherits class CIRCLE twice virtually. Since the class CIRCLE is virtually inherited twice, there is only one object of class CIRCLE in the derived class PATTERN. This is the simplest use of virtual inheritance and is not particularly useful. One skilled in the art would appreciate virtual inheritance can be very useful when the class derivation is more complex.
A class may also specify whether its function members are to be virtually inherited. Declaring that a function member is virtual means that the function can be overridden by a function of the same name and type in a derived class. In the following example, the function draw is declared to be virtual in classes CIRCLE and CIRCLE.sub.-- FILL.
______________________________________ class CIRCLE { int x, y; int radius; virtual void draw(); }; class CIRCLE.sub.-- FILL : CIRCLE { int pattern; virtual void draw(); }; ______________________________________
Continuing with the example, the following statement declares object a to be of type class CIRCLE and object b to be of type class CIRCLE.sub.-- FILL.
CIRCLE a;
CIRCLE.sub.-- FILL b;
The following statement refers to the function draw as defined in class CIRCLE.
a.draw();
Whereas, the following statement refers to the function draw defined in class CIRCLE.sub.-- FILL.
b.draw();
Moreover, the following statements type cast object b to an object of type class CIRCLE and invoke the function draw that is defined in class CIRCLE.sub.-- FILL.
______________________________________ CIRCLE c; c.sub.-- ptr *CIRCLE; c.sub.-- ptr = &b; c.sub.-- ptr-&gt;draw(); // CIRCLE.sub.-- FILL::draw() ______________________________________
Thus, the type casting preserves the call to the overriding function CIRCLE.sub.-- FILL::draw.
The C++ language provides for the automatic initialization during construction of an object at run time. A special class member function (a constructor) is invoked implicitly whenever an object is declared or allocated through a "new" operator. A constructor is a user-supplied initialization function that has the same name as that of the class. In the following example, a constructor (function member CIRCLE) is defined for class CIRCLE.
______________________________________ class CIRCLE { int x, y; int radius; virtual void draw(); CIRCLE() {x=2; y=2; radius=1}; }; ______________________________________
Continuing with the example, the following statement declares object a to be of type class CIRCLE and initializes the data members x, y, and radius to 2, 2, and 1, respectively. During construction, the constructor CIRCLE is invoked to perform the initialization.
CIRCLE a;
If the constructor was not supplied for class CIRCLE, then an object could be initialized explicitly by the following statements.
______________________________________ CIRCLE a; a.x = 2; a.y = 2; a.radius = 1; ______________________________________
A constructor may invoke a non-virtual or a virtual function member of the class or of a base class of the class. In the following example, the constructor for class CIRCLE invokes the virtual function clear.
______________________________________ class CIRCLE { int x, y; int radius; virtual void draw(); virtual void clear() {x=0; y=0; radius=0}; CIRCLE() {clear()}; }; ______________________________________
The virtual function member clear sets each of the data members to a 0 value. The constructor invokes function member clear to initialize the data members of the object. The virtual function member clear could be explicitly invoked after initialization to clear the data members.
As described above, the class CIRCLE may be virtually inherited and the virtual function members of class CIRCLE may be overridden in the derived class. In the following example, class CIRCLE.sub.-- FILL virtually inherits class CIRCLE, overrides virtual function member clear, and defines a constructor.
______________________________________ class CIRCLE.sub.-- FILL : virtual CIRCLE { int pattern; void fill(); virtual void clear () {pattern=0}; CIRCLE.sub.-- FILL () {clear()}; ______________________________________
The function member clear sets the data member pattern to a zero value. During initialization of an object of type class CIRCLE.sub.-- FILL, the constructor for class CIRCLE is invoked to initialize the occurrence of the class CIRCLE object in the CIRCLE.sub.-- FILL object. Then, the constructor for class CIRCLE.sub.-- FILL is invoked to initialize the CIRCLE.sub.-- FILL object. From the above description, it might be anticipated that the constructor CIRCLE would invoke the function member CIRCLE.sub.-- FILL::clear because it overrides the function member CIRCLE::clear. Recall that as described above, normally the overriding functions are invoked for an object. If this were the case with the constructors, then after an object of type class CIRCLE.sub.-- FILL was initialized, it would have the data member pattern initialized, but data members x, y, and radius would not be initialize. Although this result might be expected, it might be desirable to have all the data members initialized.
To achieve this desired result (and other results that one skilled in the art would be aware of, such as, not accessing unconstructed objects during construction), the C++ language specifies that a constructor that invokes a virtual function will not invoke an overriding function, but rather invokes the function defined in the class of the constructor. Thus, in the above example, when an object of type class CIRCLE.sub.-- FILL is initialized, the constructor for class CIRCLE invokes function member CIRCLE::clear, rather than CIRCLE.sub.-- FILL::clear. After initialization is complete, each of the data member of the object will be initialized to 0.